﻿using FishNet.Connection;
using FishNet.Managing;
using FishNet.Managing.Statistic;
using FishNet.Managing.Transporting;
using FishNet.Transporting;
using GameBasic;
using System.Collections;
using UnityEngine;

namespace VCity
{
    public class NetworkMain : MonoSingleton<NetworkMain>
    {
        public NetworkManager networkManager;

        //
        [Header("Network")]
        public NetServer serverPrefab;
        public NetPlayer playerPrefab;

        public void Init(ConfigData config)
        {
            // network
            var t = networkManager.TransportManager.Transport;
            t.SetClientAddress(config.ip);
            t.SetPort((ushort)config.port);
        }

        protected override void OnDestroy()
        {
            base.OnDestroy();

            if (networkManager != null)
            {
                var server = networkManager.ServerManager;
                server.OnServerConnectionState -= OnServerConnectionState;
                server.OnRemoteConnectionState -= OnServerRemoteState;
                networkManager.StatisticsManager.NetworkTraffic.OnServerNetworkTraffic -= NetworkTraffic_OnServerNetworkTraffic;

                var client = networkManager.ClientManager;
                client.OnClientConnectionState -= OnClientConnectionState;
                client.OnRemoteConnectionState -= OnClientRemoteConnectionState;
            }
        }

        #region Init
        void InitServer()
        {
            var server = networkManager.ServerManager;
            server.OnServerConnectionState += OnServerConnectionState;
            server.OnRemoteConnectionState += OnServerRemoteState;
            //server.OnAuthenticationResult += Server_OnAuthenticationResult;
            networkManager.StatisticsManager.NetworkTraffic.OnServerNetworkTraffic += NetworkTraffic_OnServerNetworkTraffic;

            var netServer = Instantiate(serverPrefab, transform);
            netServer.matchPlayerSize = Preloader.Instance.config.matchPlayerSize;
        }

        void InitClient()
        {
            var client = networkManager.ClientManager;
            client.OnClientConnectionState += OnClientConnectionState;
            client.OnRemoteConnectionState += OnClientRemoteConnectionState;
        }

        void OnServerConnectionState(ServerConnectionStateArgs args)
        {
            var transport = networkManager.GetComponent<TransportManager>().Transport;

            switch (args.ConnectionState)
            {
                case LocalConnectionState.Stopped:
                    Debug.Log("Server: stopped");
                    break;
                case LocalConnectionState.Starting:
                    break;
                case LocalConnectionState.Started:
                    Debug.Log($"Server: started on {transport.GetServerBindAddress(IPAddressType.IPv4)}:{transport.GetPort()}");
                    break;
                case LocalConnectionState.Stopping:
                    break;
                default:
                    break;
            }
        }

        void OnServerRemoteState(NetworkConnection connection, RemoteConnectionStateArgs args)
        {
            switch (args.ConnectionState)
            {
                case RemoteConnectionState.Stopped:
                    //Debug.Log($"Server: ({connection.ClientId}){connection.GetAddress()} disconnected");
                    break;
                case RemoteConnectionState.Started:
                    //Debug.Log($"Server: ({connection.ClientId}){connection.GetAddress()} connnected");
                    break;
                default:
                    break;
            }
        }

        void OnClientConnectionState(ClientConnectionStateArgs args)
        {
            var transport = networkManager.GetComponent<TransportManager>().Transport;
            switch (args.ConnectionState)
            {
                case LocalConnectionState.Stopped:
                    Debug.Log("Client: disconnected");
                    break;
                case LocalConnectionState.Starting:
                    break;
                case LocalConnectionState.Started:
                    Debug.Log($"Client: connected to server: {transport.GetClientAddress()}:{transport.GetPort()}");
                    break;
                case LocalConnectionState.Stopping:
                    break;
                default:
                    break;
            }
        }

        private void OnClientRemoteConnectionState(RemoteConnectionStateArgs args)
        {
            /*
            var transport = networkManager.GetComponent<TransportManager>().Transport;
            switch (args.ConnectionState)
            {
                case RemoteConnectionState.Stopped:
                    break;
                case RemoteConnectionState.Started:
                    // logged in successed
                    break;
                default:
                    break;
            }
            */
        }

        public void StartServer()
        {
            InitServer();
            networkManager.ServerManager.StartConnection();
        }

        public void StartClient()
        {
            InitClient();
            //networkManager.ClientManager.StartConnection();
        }

        public void StartHost()
        {
            StartServer();
            StartClient();
        }
        #endregion

        #region Login

        public IEnumerator Client_TryLogin(NetLoginData data, System.Action<bool> callback)
        {
            if (NetPlayer.Instance == null)
            {
                Debug.Log("Try login");
                // start connection
                if (!networkManager.ClientManager.Started)
                {
                    networkManager.ClientManager.StartConnection();

                    // wait connection
                    float time = 0;
                    while (!networkManager.ClientManager.Started && time < 1)
                    {
                        time += Time.deltaTime;
                        yield return null;
                    }
                }

                // authtication
                networkManager.ClientManager.Broadcast(data);
                bool success;
                // wait
                {
                    float time = 0;
                    while (!(success = networkManager.ClientManager.Connection.IsActive) && time < 1)
                    {
                        time += Time.deltaTime;
                        yield return null;
                    }
                }

                // inform complete
                callback?.Invoke(success);
            }
        }
        #endregion

        private void NetworkTraffic_OnServerNetworkTraffic(NetworkTrafficArgs obj)
        {
            Debug.Log($"In:{obj.ToServerBytes} Out:{obj.FromServerBytes}");
        }
    }
}